#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMessageBox>
#include <QSerialPortInfo>
#include <QFileDialog>
#include <QDateTime>
#include <QThread>

// 构造函数：初始化 Widget 界面并配置串口相关设置
Widget::Widget(QWidget *parent)
    : QWidget(parent)  // 调用父类构造函数
    , ui(new Ui::Widget)  // 创建 UI 对象
{
    // 从 .ui 文件加载界面布局及控件
    ui->setupUi(this);

    // 设置主布局为 gridLayoutGlobal（在.ui中定义）
    this->setLayout(ui->gridLayoutGlobal);

    buttonIndex = 0;
    // 初始化发送和接收字节计数器为0
    writeCntTotal = 0;
    readCntTotal = 0;

    // 初始串口状态为关闭状态
    serialStatus = false;

    // 默认禁用发送按钮（因为串口未打开）
    ui->btnSendContext->setEnabled(false);

    // 默认禁用定时发送复选框（checkBSendInTime）
    ui->checkBSendInTime->setEnabled(false);

    // 同时禁用“自动换行”和“十六进制发送”选项
    ui->checkBSendNewLine->setEnabled(false);
    ui->checkBHexSend->setEnabled(false);

    // 创建一个新的 QSerialPort 对象用于串口通信
    serialPort = new QSerialPort(this);

    // 创建定时器用于刷新时间显示
    QTimer *getSysTimeTimer = new QTimer(this);

    // 连接定时器超时信号到 time_reflash 槽函数（每秒刷新一次时间）
    connect(getSysTimeTimer, SIGNAL(timeout()), this, SLOT(time_reflash()));

    // 启动定时器，间隔为1000毫秒（即1秒）
    getSysTimeTimer->start(1000);

    // 创建定时器对象，用于实现定时发送功能
    timer = new QTimer(this);

    myThread = new CustomThread(this);

    connect(myThread, &CustomThread::threadTimeout, this, &Widget::buttons_handler);

    //    buttonsConTimer = new QTimer(this);

    //    connect(buttonsConTimer, &QTimer::timeout, this, &Widget::buttons_handler);

    // 连接串口的 readyRead 信号到槽函数 on_SerialData_readyToRead，
    // 当串口有可读数据时会触发该槽函数
    connect(serialPort, &QSerialPort::readyRead, this, &Widget::on_SerialData_readyToRead);

    // 连接定时器超时信号到 lambda 表达式，用于触发发送按钮点击事件
    connect(timer, &QTimer::timeout, [=](){
        on_btnSendContext_clicked();
    });

    connect(ui->comboBox_serialNum, &MyComboBox::refresh, this, &Widget::refreshSerialName);

    // 设置波特率下拉框默认选中第7个选项（索引从0开始，第6个元素）
    ui->comboBox_boautrate->setCurrentIndex(6);

    // 设置数据位下拉框默认选中第4个选项（索引从0开始，第3个元素）
    ui->comboBox_databit->setCurrentIndex(3);

    refreshSerialName();

    // 设置初始发送状态标签文本为 "串口号NotOpen!"
    ui->labelSendStatus->setText(ui->comboBox_serialNum->currentText() + "NotOpen!");



    // 循环遍历编号从 1 到 9 的按钮控件
    for (int i = 1; i <= 9; i++) {
        // 构造按钮控件的名称，如 "pushButton_1"、"pushButton_2" 等
        QString btnName = QString("pushButton_%1").arg(i);

        // 使用 findChild 查找当前 Widget 中对应名称的 QPushButton 控件
        QPushButton *btn = findChild<QPushButton *>(btnName);

        // 如果找到了对应的按钮控件
        if (btn) {
            // 设置按钮的自定义属性 "buttonId" 为当前循环编号 i
            btn->setProperty("buttonId", i);

            // 将该按钮加入 buttons 列表中（可后续统一管理或操作）
            buttons.append(btn);

            // 连接该按钮的 clicked() 信号到统一的槽函数 on_command_button_clicked()
            connect(btn, SIGNAL(clicked()), this, SLOT(on_command_button_clicked()));
        }

        QString lineEditName = QString("lineEdit_%1").arg(i);
        QLineEdit *lineEdit = findChild<QLineEdit *>(lineEditName);
        lineEdits.append(lineEdit);

        QString checkBoxName = QString("checkBox_%1").arg(i);
        QCheckBox *checkBox = findChild<QCheckBox *>(checkBoxName);
        checkBoxs.append(checkBox);
    }
}

// 析构函数：释放资源
Widget::~Widget()
{
    delete ui; // 删除UI对象
}

/*
    enum Parity {
        NoParity = 0,
        EvenParity = 2,
        OddParity = 3,
        SpaceParity = 4,
        MarkParity = 5,
        UnknownParity = -1
    };
    */

// 按钮点击事件处理函数：打开或关闭串口
void Widget::on_btnCloseOrOpenSerial_clicked()
{
    // 此函数被保留但未使用，可能是旧版本逻辑，当前被重载函数替代
}

// 发送按钮点击事件处理函数：发送文本框中的内容
void Widget::on_btnSendContext_clicked()
{
    int writeCnt = 0; // 用于记录实际写入串口的字节数

    // 将发送文本框中的内容转换为本地编码（如 GBK 或 UTF-8）的 C 风格字符串
    const char* sendData = ui->lineEditSendContext->text().toLocal8Bit().constData();

    // 判断是否启用“十六进制发送”模式
    if(ui->checkBHexSend->isChecked()){
        // 获取发送框中的文本内容
        QString tmp = ui->lineEditSendContext->text();

        // 1. 检查输入长度是否为偶数（因为每两个字符表示一个字节）
        QByteArray tmpArray = tmp.toLocal8Bit();
        if(tmpArray.size() % 2 != 0){
            // 输入长度不合法，设置状态标签提示错误
            ui->labelSendStatus->setText("Error Input!");
            return;
        }

        // 2. 检查输入是否全部为十六进制字符（0-9, a-f, A-F）
        for(char c : tmpArray){
            if(!std::isxdigit(c)){
                // 存在非十六进制字符，提示错误
                ui->labelSendStatus->setText("Error Input!");
                return;
            }
        }

        // 如果勾选了“自动换行”，则在数据末尾追加 \r\n
        if(ui->checkBSendNewLine->isChecked())
            tmpArray.append("\r\n");

        // 3. 将十六进制字符串转换为实际字节数据进行发送
        QByteArray arraySend = QByteArray::fromHex(tmpArray);
        writeCnt = serialPort->write(arraySend); // 写入串口
    }else{
        // 未勾选十六进制发送，按普通文本发送

        // 如果勾选了“自动换行”，则构造包含换行符的数据包
        if(ui->checkBSendNewLine->isChecked()){
            QByteArray arraySendData(sendData, strlen(sendData));
            arraySendData.append("\r\n");
            writeCnt = serialPort->write(arraySendData); // 发送带换行的内容
        }else{
            // 不带换行直接发送原始数据
            writeCnt = serialPort->write(sendData);
        }
    }

    // 判断写入是否成功
    if(writeCnt == -1){
        // 写入失败，更新发送状态为错误
        ui->labelSendStatus->setText("SendError!");
    }else{
        // 写入成功，更新总发送字节数统计
        writeCntTotal += writeCnt;

        // 打印调试信息
        qDebug() << "send OK" << sendData;

        // 更新发送状态为“发送成功”
        ui->labelSendStatus->setText("SendOK!");

        // 显示更友好的发送字节数统计格式
        ui->labelSendcnt->setText("Sent:" + QString::number(writeCntTotal));

        // 如果本次发送的内容与上次不同，则追加到发送记录中
        if(strcmp(sendData, sendBak.toStdString().c_str()) != 0){
            // 在记录框中追加发送的内容
            ui->textEditRecord->append(sendData);

            // 保存当前发送内容作为下一次比较的依据
            sendBak = QString::fromUtf8(sendData);
        }
    }
}

// 当串口有可读数据时触发此槽函数
void Widget::on_SerialData_readyToRead()
{
    // 读取串口缓冲区中所有可读的数据（一次性读取）
    QString revMassage = serialPort->readAll();

    // 如果收到的数据非空，则进行后续处理
    if(revMassage != NULL){
        // 如果勾选了“自动换行”选项，则在接收到的消息末尾追加换行符
        if(ui->checkBLine->isChecked())
            revMassage.append("\r\n");

        // 检查是否勾选了“以十六进制显示”选项
        if(ui->checkBHexDisplay->isChecked()){
            // 将收到的消息字符串转换为 UTF-8 编码的字节数组，
            // 再将其转换为大写的十六进制表示形式
            QByteArray tmpHexString = revMassage.toUtf8().toHex().toUpper();

            // 获取当前 textEditRev 控件中的内容（假设是已有的十六进制格式字符串）
            QString tmpStringHex = ui->textEditRev->toPlainText();

            // 将旧的十六进制字符串与新的十六进制数据拼接在一起
            tmpHexString = tmpStringHex.toUtf8() + tmpHexString;

            // 最后将拼接后的完整十六进制字符串设置回 textEditRev 控件中显示
            ui->textEditRev->setText(QString::fromUtf8(tmpHexString));
        }else{
            // 如果没有勾选“以十六进制显示”选项，则根据时间戳选项进一步处理

            if(ui->checkBrevTime->checkState() == Qt::Unchecked){
                // 若未勾选“显示时间戳”选项，则直接将接收到的内容插入到接收框中
                ui->textEditRev->insertPlainText(revMassage);
            }else if (ui->checkBrevTime->checkState() == Qt::Checked) {
                // 若勾选了“显示时间戳”选项，则先获取当前系统时间，
                // 然后将时间和消息一起插入到接收框中
                getSysTime();
                ui->textEditRev->insertPlainText("【" + myTime + "】" + revMassage);
            }
        }

        // 更新总接收字节数统计
        readCntTotal += revMassage.size();

        // 在界面上显示更友好的接收字节数统计信息
        ui->labelRevcnt->setText("Received:" + QString::number(readCntTotal));

        // 打印收到的消息内容（调试用，已被注释）
        qDebug() << "getMassage:" << revMassage;

        // 打印收到的消息

        ui->textEditRev->moveCursor(QTextCursor::End);
        ui->textEditRev->ensureCursorVisible();
//        ui->textEditRev->setFocus();
    }
}

// 重载的 on_btnCloseOrOpenSerial_clicked 函数，带 bool 参数表示按钮状态
void Widget::on_btnCloseOrOpenSerial_clicked(bool checked)
{
    // 输出调试信息，显示按钮是否被选中
    qDebug() << "none checked" << checked;

    if(checked){
        // 当按钮被选中时（即尝试打开串口）

        // 1. 选择端口号
        serialPort->setPortName(ui->comboBox_serialNum->currentText());

        // 2. 配置波特率
        serialPort->setBaudRate(ui->comboBox_boautrate->currentText().toInt());

        // 3. 配置数据位
        serialPort->setDataBits(QSerialPort::DataBits(
                                    ui->comboBox_databit->currentText().toUInt()));

        // 4. 配置校验位
        switch (ui->comboBox_checkbit->currentIndex()) {
        case 0 :
            serialPort->setParity(QSerialPort::NoParity);
            break;
        case 2 :
            serialPort->setParity(QSerialPort::EvenParity);
            break;
        case 3 :
            serialPort->setParity(QSerialPort::OddParity);
            break;
        case 4 :
            serialPort->setParity(QSerialPort::SpaceParity);
            break;
        case 5 :
            serialPort->setParity(QSerialPort::MarkParity);
            break;
        default:
            serialPort->setParity(QSerialPort::UnknownParity);
            break;
        }

        // 5. 配置停止位
        serialPort->setStopBits(QSerialPort::StopBits(
                                    ui->comboBox_stopbit->currentText().toUInt()));

        // 6. 流控配置
        if(ui->comboBox_fileCon->currentText() == "None")
            serialPort->setFlowControl(QSerialPort::NoFlowControl);

        // 7. 尝试打开串口
        if(serialPort->open(QIODevice::ReadWrite)){
            qDebug() << "serial open successful";

            // 禁用相关配置项防止更改
            ui->comboBox_databit->setEnabled(false);
            ui->comboBox_boautrate->setEnabled(false);
            ui->comboBox_checkbit->setEnabled(false);
            ui->comboBox_fileCon->setEnabled(false);
            ui->comboBox_serialNum->setEnabled(false);
            ui->comboBox_stopbit->setEnabled(false);

            // 改变按钮文字为“关闭串口”
            ui->btnCloseOrOpenSerial->setText("关闭串口");

            // 启用发送按钮
            ui->btnSendContext->setEnabled(true);

            // 启用定时发送复选框
            ui->checkBSendInTime->setEnabled(true);

            ui->checkBSendNewLine->setEnabled(true);
            ui->checkBHexSend->setEnabled(true);
            ui->labelSendStatus->setText(ui->comboBox_serialNum->currentText() + " isOpen!");

        }else{
            // 如果打开失败，弹出错误提示框
            QMessageBox msgBox;
            msgBox.setWindowTitle("打开串口错误");
            msgBox.setText("打开失败！串口可能被占用或者已经被拔出！");
            msgBox.exec();
        }
    }else{
        // 当按钮未被选中时（即尝试关闭串口）

        // 关闭串口
        serialPort->close();

        // 改变按钮文字为“打开串口”
        ui->btnCloseOrOpenSerial->setText("打开串口");

        // 启用所有配置项
        ui->comboBox_databit->setEnabled(true);
        ui->comboBox_boautrate->setEnabled(true);
        ui->comboBox_checkbit->setEnabled(true);
        ui->comboBox_fileCon->setEnabled(true);
        ui->comboBox_serialNum->setEnabled(true);
        ui->comboBox_stopbit->setEnabled(true);

        // 禁用发送按钮
        ui->btnSendContext->setEnabled(false);

        // 取消勾选定时发送复选框，并禁用该选项
        ui->checkBSendInTime->setEnabled(false);
        ui->checkBSendInTime->setCheckState(Qt::Unchecked);

        // 停止定时器
        timer->stop();

        // 启用时间间隔输入框和发送内容输入框
        ui->lineEditTimeeach->setEnabled(true);
        ui->lineEditSendContext->setEnabled(true);

        ui->checkBSendNewLine->setEnabled(false);
        ui->checkBHexSend->setEnabled(false);
        ui->labelSendStatus->setText(ui->comboBox_serialNum->currentText() + " isClose!");
    }
}

// 复选框点击事件处理函数：定时发送功能
void Widget::on_checkBSendInTime_clicked(bool checked)
{
    // 当前为空实现，后续可用于实现定时发送功能
    qDebug() << "checkedBsendIntiome:" << checked;
    if(checked){
        // 禁用时间间隔输入框和发送内容输入框
        ui->lineEditTimeeach->setEnabled(false);
        ui->lineEditSendContext->setEnabled(false);
        // 启动定时器，时间间隔由用户输入决定
        timer->start(ui->lineEditTimeeach->text().toInt());
        //        on_btnSendContext_clicked();
    }else{
        // 停止定时器
        timer->stop();
        // 启用时间间隔输入框和发送内容输入框
        ui->lineEditTimeeach->setEnabled(true);
        ui->lineEditSendContext->setEnabled(true);
    }
}

// 清除接收区域按钮点击事件处理函数
void Widget::on_btnrevClear_clicked()
{
    // 清空接收区文本编辑控件（textEditRev）中的所有内容
    ui->textEditRev->setText("");
}


// 保存接收数据按钮点击事件处理函数
void Widget::on_btnrevSave_clicked()
{
    // 弹出文件保存对话框，让用户选择保存路径和文件名
    // 默认路径为 "E:/QT/serialData.txt"，默认文件名为 serialData.txt，支持 txt 格式
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
                                                    "E:/QT/serialData.txt",
                                                    tr("Text (*.txt)"));

    // 如果用户没有点击取消，即获取到了有效的文件路径
    if(fileName != NULL){
        // 创建 QFile 对象，用于操作目标文件
        QFile file(fileName);

        // 以只写模式和文本格式打开文件，如果打开失败则返回
        if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
            return;

        // 创建 QTextStream 对象，用于向文件写入文本内容
        QTextStream out(&file);

        // 将接收区域（textEditRev）中的纯文本内容写入到文件中
        out << ui->textEditRev->toPlainText();

        // 关闭文件
        file.close();
    }
}

/*
 * 定时刷新时间显示的槽函数
 * 调用 getSysTime() 获取当前系统时间，并更新到界面上的 labelCurrentTime 控件
 */
void Widget::time_reflash()
{
    // 获取当前系统时间，并将结果保存在成员变量 myTime 中
    getSysTime();

    // 将获取到的时间字符串设置到界面上的 labelCurrentTime 标签中显示
    ui->labelCurrentTime->setText(myTime);
}


/*
 * 获取当前系统时间并格式化为指定格式的字符串（YYYY-MM-DD HH:MM:SS）
 */
void Widget::getSysTime()
{
    // 获取当前日期时间对象
    QDateTime currentTime = QDateTime::currentDateTime();

    // 提取当前日期部分
    QDate date = currentTime.date();

    // 从日期中提取年、月、日
    int year = date.year();
    int month = date.month();
    int day = date.day();

    // 提取当前时间部分
    QTime time = currentTime.time();
    int hour = time.hour();     // 小时
    int minute = time.minute(); // 分钟
    int second = time.second(); // 秒

    // 将时间信息格式化为 "YYYY-MM-DD HH:MM:SS" 字符串格式
    // 使用 arg 方法进行格式化，每个数字都保证至少两位，不足补零
    myTime = QString("%1-%2-%3 %4:%5:%6")
            .arg(year, 2, 10, QChar('0'))     // 年份（两位显示）
            .arg(month, 2, 10, QChar('0'))    // 月份（两位显示）
            .arg(day, 2, 10, QChar('0'))      // 日（两位显示）
            .arg(hour, 2, 10, QChar('0'))     // 小时（两位显示）
            .arg(minute, 2, 10, QChar('0'))   // 分钟（两位显示）
            .arg(second, 2, 10, QChar('0'));  // 秒（两位显示）
}

void Widget::on_checkBHexDisplay_clicked(bool checked)
{
    if(checked){
        // 当复选框被选中时（启用十六进制显示模式）

        // 1. 从 textEditRev 控件中读取当前显示的纯文本内容
        QString tmp = ui->textEditRev->toPlainText();

        // 2. 将读取到的文本转换为 UTF-8 编码的字节数组，
        // 然后将该字节数组转换为对应的十六进制字符串表示（小写格式）
        QByteArray qtemp = tmp.toUtf8();     // 将 QString 转换为 QByteArray（UTF-8 格式）
        qtemp = qtemp.toHex();               // 将字节数组转换为十六进制字符串（如 "48656c6c6f"）

        QString lastshow;
        tmp = QString::fromUtf8(qtemp);      // 将 QByteArray 类型的十六进制数据转回 QString

        // 3. 对转换后的十六进制字符串进行格式化处理：
        // 每两个字符一组进行分割，并在每组之间添加空格，提高可读性
        for (int i = 0; i < tmp.size(); i += 2) {
            lastshow += tmp.mid(i, 2) + " ";
        }

        // 最后将带有空格分隔的十六进制字符串转换为大写形式，
        // 并设置回 textEditRev 控件中以供显示
        ui->textEditRev->setText(lastshow.toUpper());
    }else{
        // 当复选框未被选中时（关闭十六进制显示，恢复为普通文本显示）

        // 1. 从 textEditRev 控件中读取当前显示的内容（假设是十六进制格式字符串）
        QString tmpHexString = ui->textEditRev->toPlainText();

        // 2. 将读取到的十六进制字符串转换为 UTF-8 编码的字节数组
        QByteArray tmpHexQBytearray = tmpHexString.toUtf8();

        // 使用 QByteArray::fromHex 方法将十六进制编码的字节数组转换回原始的二进制字节数组
        QByteArray tmpQbyteString = QByteArray::fromHex(tmpHexQBytearray);

        // 3. 将转换回的原始二进制数据转换为 QString 字符串，
        // 并重新设置回 textEditRev 控件中，以便以普通文本形式显示
        ui->textEditRev->setText(QString::fromUtf8(tmpQbyteString));
    }
    ui->textEditRev->moveCursor(QTextCursor::End);
    ui->textEditRev->ensureCursorVisible();
}

/*
     * 当点击 "btnhideTable" 按钮时触发的槽函数
     * 用于切换是否隐藏/显示 groupBoxTexts 面板
     */
void Widget::on_btnhideTable_clicked(bool checked)
{
    if(checked){
        // 如果按钮处于选中状态（即当前要隐藏面板）

        // 修改按钮文本为“拓展面板”
        ui->btnhideTable->setText("拓展面板");

        // 隐藏 groupBoxTexts 控件
        ui->groupBoxTexts->hide();
    }else{
        // 如果按钮处于未选中状态（即当前要显示面板）

        // 修改按钮文本为“隐藏面板”
        ui->btnhideTable->setText("隐藏面板");

        // 显示 groupBoxTexts 控件
        ui->groupBoxTexts->show();
    }
}


/*
     * 当点击 "btnHideHistory" 按钮时触发的槽函数
     * 用于切换是否隐藏/显示 groupBoxRecord 历史记录面板
     */
void Widget::on_btnHideHistory_clicked(bool checked)
{
    if(checked){
        // 如果按钮处于选中状态（即当前要隐藏历史记录面板）

        // 修改按钮文本为“显示历史”
        ui->btnHideHistory->setText("显示历史");

        // 隐藏 groupBoxRecord 控件
        ui->groupBoxRecord->hide();
    }else{
        // 如果按钮处于未选中状态（即当前要显示历史记录面板）

        // 修改按钮文本为“隐藏历史”
        ui->btnHideHistory->setText("隐藏历史");

        // 显示 groupBoxRecord 控件
        ui->groupBoxRecord->show();
    }
}

/*
 * 刷新串口名称下拉框内容的槽函数
 * 清空当前下拉框中的串口号列表，并重新枚举系统中所有可用串口
 */
void Widget::refreshSerialName()
{
    // 清空 comboBox_serialNum 下拉框中的所有已有串口号
    ui->comboBox_serialNum->clear();

    // 获取系统当前所有可用串口信息
    QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts();

    // 遍历所有可用串口，并将串口号添加到 comboBox_serialNum 下拉框中
    for (QSerialPortInfo serialInfo : serialList) {
        qDebug() << serialInfo.portName(); // 打印当前串口号，用于调试
        ui->comboBox_serialNum->addItem(serialInfo.portName()); // 添加串口号到下拉列表
    }

    // 更新发送状态标签，提示用户串口列表已刷新
    ui->labelSendStatus->setText("Com Refresh!");
}

/*
 * 自定义命令按钮点击事件处理函数
 * 用于统一处理多个发送按钮的点击操作
 * 根据按钮对象的属性获取编号，并动态定位对应的输入框和复选框控件
 */
void Widget::on_command_button_clicked()
{
    // 获取触发该槽函数的按钮对象（发送者）
    QPushButton *btn = qobject_cast<QPushButton *>(sender());

    // 如果成功获取到按钮对象
    if (btn) {
        // 从按钮的自定义属性中获取 "buttonId"，并转换为整型编号
        int num = btn->property("buttonId").toInt();

        // 构造对应的 lineEdit 控件名称，如 "lineEdit_3"
        QString lineEditName = QString("lineEdit_%1").arg(num);

        // 查找对应的 QLineEdit 输入框控件
        QLineEdit *lineEdit = findChild<QLineEdit *>(lineEditName);

        // 如果找到该输入框控件，则将其文本设置为主发送框的内容
        if (lineEdit){
            if(lineEdit->text().size() <= 0){
                return;
            }
            ui->lineEditSendContext->setText(lineEdit->text());

        }

        // 构造对应的 checkBox 控件名称，如 "checkBox_3"
        QString checkBoxName = QString("checkBox_%1").arg(num);

        // 查找对应的 QCheckBox 复选框控件
        QCheckBox *checBox = findChild<QCheckBox *>(checkBoxName);

        // 如果找到该复选框控件，则将“十六进制发送”选项设置为其当前状态
        if (checBox)
            ui->checkBHexSend->setChecked(checBox->isChecked());

        // 调用通用的发送按钮点击函数，执行实际的数据发送操作
        on_btnSendContext_clicked();
    }
}

/**
 * buttons_handler 函数
 * 用于按顺序模拟点击 buttons 列表中的按钮（常用于自动发送多个预设命令）
 */
void Widget::buttons_handler()
{
    // 如果当前索引值小于按钮列表的大小，说明还有按钮未被点击
    if (buttonIndex < buttons.size()) {
        // 获取当前索引对应的按钮对象
        QPushButton *btntmp = buttons[buttonIndex];

        // 手动发射该按钮的 clicked() 信号，触发其绑定的槽函数（如 on_command_button_clicked）
        emit btntmp->clicked();

        // 索引递增，准备点击下一个按钮
        buttonIndex++;
    } else {
        // 所有按钮都已点击完毕，重置索引为 0，以便下一轮循环使用
        buttonIndex = 0;
    }

    // 注释掉的延时操作：不能在主线程中使用 QThread::msleep，会导致界面卡顿或刷新异常
    // QThread::msleep(ui->spinBox->text().toUInt());
}


/**
 * on_checkBox_send_clicked 函数
 * 当“连续发送”复选框状态改变时调用
 * 根据复选框是否被选中来启动或停止后台线程（myThread），并控制spinBox的启用/禁用状态
 *
 * @param checked 当前复选框是否被选中
 */
void Widget::on_checkBox_send_clicked(bool checked)
{
    if (checked) {
        // 如果复选框被选中，则禁用 spinBox 控件以防止用户修改定时时间
        ui->spinBox->setEnabled(false);

        // 启动后台线程 myThread，该线程可能用于执行按钮点击模拟等操作
        myThread->start();

        // 注释掉的代码：原计划使用定时器实现自动发送功能
        // buttonsConTimer->start(ui->spinBox->text().toUInt());
    } else {
        // 如果复选框未被选中，则重新启用 spinBox 控件允许用户修改定时时间
        ui->spinBox->setEnabled(true);

        // 终止后台线程 myThread 的运行
        myThread->terminate();

        // 注释掉的代码：原计划使用定时器实现自动发送功能
        // buttonsConTimer->stop();
    }
}

/**
 * on_btnInit_clicked 函数
 * 当用户点击“初始化”按钮时触发
 * 弹出确认对话框，询问用户是否重置所有配置项（不可逆操作）
 */
void Widget::on_btnInit_clicked()
{
    // 创建一个消息提示框对象
    QMessageBox msgBox;

    // 设置消息框的标题为“提示”
    msgBox.setWindowTitle("提示");

    // 设置消息框的图标类型为问题图标（问号）
    msgBox.setIcon(QMessageBox::Question);

    // 设置消息框的主文本内容：提示用户此操作不可逆
    msgBox.setText("重置列表不可逆，确认是否重置?");

    // 手动添加两个按钮：“是” 和 “否”，并指定其对应的角色
    QPushButton* yesButton = msgBox.addButton("是", QMessageBox::YesRole);
    QPushButton* noButton = msgBox.addButton("否", QMessageBox::NoRole);

    // 显示模态对话框，并进入局部事件循环等待用户操作
    msgBox.exec();

    // 判断用户点击的是哪个按钮
    if (msgBox.clickedButton() == yesButton) {
        // 用户点击了“是”，执行重置操作

        qDebug() << "yesButton";

        // 遍历 lineEdits 列表中的每一个 QLineEdit 控件
        for (int i = 0; i <= lineEdits.size(); i++) {
            // 清空当前 QLineEdit 中的内容
            lineEdits[i]->clear();

            // 取消对应位置的 QCheckBox 的勾选状态
            checkBoxs[i]->setChecked(false);
        }

    } else if (msgBox.clickedButton() == noButton) {
        // 用户点击了“否”，不执行任何操作，仅打印日志
        qDebug() << "noButton";
    }
}


/**
 * on_btnSave_clicked 函数
 * 当用户点击“保存”按钮时触发
 * 将界面上预设命令及其启用状态保存到一个文本文件中
 */
void Widget::on_btnSave_clicked()
{
    // 打开系统文件保存对话框，让用户选择保存路径和文件名
    QString fileName = QFileDialog::getSaveFileName(this, tr("保存文件"),
                                                    "E:/QT/serial-debugging-assistant",
                                                    tr("文本类型 (*.txt)"));

    // 创建 QFile 对象，用于操作目标文件
    QFile file(fileName);

    // 以只写且文本模式打开文件，若打开失败则直接返回，不执行后续操作
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
        return;

    // 创建 QTextStream 输出流对象，用于向文件写入文本内容
    QTextStream out(&file);

    // 遍历所有预设命令项（lineEdit 和 checkBox 控件列表）
    for (int i = 0; i < lineEdits.size(); i++) {
        // 写入当前命令对应的复选框状态（true/false）和命令文本内容
        // 每条记录占一行，格式为：checkBox状态 , lineEdit文本
        out << checkBoxs[i]->isChecked() << " , " << lineEdits[i]->text() << "\n";
    }

    // 关闭文件，确保数据成功写入磁盘
    file.close();
}

/**
 * on_btnLoad_clicked 函数
 * 当用户点击“加载”按钮时触发
 * 从文本文件中读取预设命令及其启用状态，并恢复到界面控件中
 */
void Widget::on_btnLoad_clicked()
{
    // 定义索引变量 i，用于遍历控件列表并逐行写入数据
    int i = 0;

    // 打开系统文件选择对话框，让用户选择要加载的文件
    QString fileName = QFileDialog::getOpenFileName(this, tr("打开文件"),
                                                    "E:/QT/serial-debugging-assistant",
                                                    tr("文本类型 (*.txt)"));

    // 如果用户选择了一个有效的文件路径
    if (fileName != NULL) {
        // 创建 QFile 对象，用于操作目标文件
        QFile file(fileName);

        // 以只读且文本模式打开文件，若失败则直接返回
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
            return;

        // 创建 QTextStream 输入流对象，用于从文件中读取文本内容
        QTextStream in(&file);

        // 按行读取文件内容，直到文件末尾或 i > 9（最多读取10条）
        while (!in.atEnd() && i <= 9) {
            // 读取当前一行的内容
            QString line = in.readLine();

            // 将该行按 " , " 分割成两个部分：checkBox状态 和 lineEdit文本
            QStringList parts = line.split(" , ");

            // 如果成功分割出两个有效部分
            if (parts.count() == 2) {
                // 设置对应位置的复选框状态（0 或 1 转为 bool）
                checkBoxs[i]->setChecked(parts[0].toInt());

                // 设置对应位置的输入框文本内容
                lineEdits[i]->setText(parts[1]);
            }

            // 控件索引递增，准备处理下一行
            i++;
        }
    }
}
